package com.njcb.ams.support.comm;

import com.njcb.ams.factory.domain.AppContext;
import com.njcb.ams.util.AmsDateUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 序号生成器
 *
 * @author liuyanlong
 */
@Service
public class SequenceService {
    private static final Logger logger = LoggerFactory.getLogger(SequenceService.class);
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private PlatformTransactionManager transactionManager;

    public static final SequenceService getInstance() {
        return AppContext.getBean(SequenceService.class);
    }

    private Map<String, SeqControl> seqValMap = new ConcurrentHashMap<String, SeqControl>();

    /**
     * 服务消费方生成的通讯流水号，由服务消费的发起方生成，在同一系统内、交易日内保证唯一。
     * 核心项目一期只启用前12位作为通讯流水号（定长报文：左对齐，不补空格或）
     * <p>
     * ESB交易流水 12位
     *
     * @return
     */
    public static String getEsbSeq() {
        return getInstance().genSeqStr("REQSEQ");
    }

    /**
     * 全局流水号；渠道层系统客户或柜员发起交易的最原始流水号，
     * 报文所经过的业务系统从服务消费方报文头中获取，在整个交易流转中保持不变.
     * <p>
     * ESB全局流水 24位
     *
     * @return
     */
    public static String getGlobalSeq() {
        String globalSeq = "GBC5" + AmsDateUtils.getCurrentDate6() + "000" + getEsbSeq().substring(3) + "00";
        return globalSeq;
    }

    /**
     * 根据valueNo配置获取序列好
     *
     * @param valueNo
     * @return
     */
    public String genSeqStr(String valueNo) {
        long currSeqNum = genSeqSyn(valueNo);
        SeqControl sc = seqValMap.get(valueNo);
        if (null == sc.valueIndex) {
            sc.valueIndex = "";
        }
        int length = sc.valueLength - sc.valueIndex.length();
        return sc.valueIndex + StringUtils.leftPad(String.valueOf(currSeqNum), length, '0');
    }

    /**
     * 获取Oracle序列
     *
     * @param sequenceName 序列名
     * @return
     */
    public static Long getSequenceByOracle(String sequenceName) {
        return getInstance().genOracleSequence(sequenceName, 1).get(0);
    }

    /**
     * 获取Oracle序列
     *
     * @param sequenceName 序列名
     * @param count        获取数量
     * @return
     */
    public static List<Long> getSequenceByOracle(String sequenceName, Integer count) {
        return getInstance().genOracleSequence(sequenceName, count);
    }

    /**
     * 获取Oracle序列
     *
     * @param sequenceName 序列名
     * @param count        获取数量
     * @return
     */
    private List<Long> genOracleSequence(String sequenceName, Integer count) {
        String sbsql = "select " + sequenceName + ".NEXTVAL from dual connect by rownum <= " + count;
        List<Long> retList = jdbcTemplate.queryForList(sbsql, Long.class);
        return retList;
    }

    private long genSeqSyn(String valueNo) {
        // 根据key值获得对应的锁.
        SeqControl objLocker = seqValMap.get(valueNo);
        // 无缓存时再同步生成，防止大量没有必要的同步代码
        if (null == objLocker) {
            synchronized (seqValMap) {
                objLocker = seqValMap.get(valueNo);
                if (null == objLocker) {
                    objLocker = new SeqControl();
                    seqValMap.put(valueNo, objLocker);
                }
            }
        }
        // 进行加锁保护.
        synchronized (objLocker) {
            SeqControl sc = seqValMap.get(valueNo);
            if (sc.freeSize > 0 && sc.currSeq < sc.maxValue) {
                sc.freeSize--;
                sc.currSeq++;
                return sc.currSeq;
            }
            sc = genControlForDb(sc, valueNo);
            sc.freeSize = sc.cacheSize - 1;
            return sc.currSeq;
        }
    }


    private SeqControl genControlForDb(SeqControl sc, String valueNo) {
        TransactionStatus status = beginTransaction();
        List<Map<String, Object>> seqMapList = jdbcTemplate.queryForList(
                "SELECT VALUE_NO,VALUE_INDEX,VALUE_LENGTH,VALUE_CURR,MAX_VALUE,MIN_VALUE,CACHE_SIZE FROM SYS_SEQ_CONTROL WHERE VALUE_NO = '"
                        + valueNo + "' FOR UPDATE ");
        if (null != seqMapList && seqMapList.size() == 1) {
            Map<String, Object> seqMap = seqMapList.get(0);
            sc.currSeq = (new BigDecimal(seqMap.get("VALUE_CURR").toString())).longValue();
            sc.valueIndex = (String) seqMap.get("VALUE_INDEX");
            sc.valueLength = (new BigDecimal(seqMap.get("VALUE_LENGTH").toString())).intValue();
            sc.cacheSize = (new BigDecimal(seqMap.get("CACHE_SIZE").toString())).intValue();
            long maxSeq = (new BigDecimal(seqMap.get("MAX_VALUE").toString())).longValue();
            sc.maxValue = maxSeq;
            long dbcurrSeq = sc.currSeq + sc.cacheSize;
            if (dbcurrSeq > maxSeq) {
                dbcurrSeq = (new BigDecimal(seqMap.get("MIN_VALUE").toString())).longValue();
            }
            // 修改记录到数据库
            jdbcTemplate.update(
                    "UPDATE SYS_SEQ_CONTROL SET VALUE_CURR = " + dbcurrSeq + " WHERE VALUE_NO = '" + valueNo + "'");
            transactionManager.commit(status);
        } else {
            logger.warn("序号生成器" + valueNo + "不存在, 自动创建序列");
            String insertSql = "insert into SYS_SEQ_CONTROL (VALUE_NO, VALUE_INDEX, VALUE_LENGTH, VALUE_CURR, MAX_VALUE, MIN_VALUE, CACHE_SIZE)" +
                    "values ('" + valueNo + "', '', 12, 1, 999999999, 1, 20)";
            jdbcTemplate.update(insertSql);
            transactionManager.commit(status);
            return genControlForDb(sc, valueNo);
        }
        return sc;
    }

    private TransactionStatus beginTransaction() {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        return transactionManager.getTransaction(def);
    }

    private class SeqControl {
        // 缓存记录数
        private int cacheSize = 1;
        // 未用缓存记录数
        private int freeSize = 0;
        // 当前记录序号
        private long currSeq = 0;
        //最大值
        private long maxValue = 0;
        private String valueIndex;
        private int valueLength;
    }
}
